package com.anji.plus.gaea.security.plus.modules.org.service.impl;

import com.anji.plus.gaea.bean.TreeNode;
import com.anji.plus.gaea.constant.BaseOperationEnum;
import com.anji.plus.gaea.constant.Enabled;
import com.anji.plus.gaea.constant.GaeaConstant;
import com.anji.plus.gaea.curd.mapper.GaeaBaseMapper;
import com.anji.plus.gaea.exception.BusinessException;
import com.anji.plus.gaea.holder.UserContentHolder;
import com.anji.plus.gaea.security.plus.event.OrgEvent;
import com.anji.plus.gaea.security.plus.modules.org.controller.dto.GaeaOrgDTO;
import com.anji.plus.gaea.security.plus.modules.org.controller.param.GaeaOrgParam;
import com.anji.plus.gaea.security.plus.modules.org.dao.GaeaOrgMapper;
import com.anji.plus.gaea.security.plus.modules.org.dao.entity.GaeaOrg;
import com.anji.plus.gaea.security.plus.modules.org.service.GaeaOrgService;
import com.anji.plus.gaea.security.plus.modules.role.dao.GaeaRoleMapper;
import com.anji.plus.gaea.security.plus.modules.role.dao.entity.GaeaRole;
import com.anji.plus.gaea.security.plus.modules.user.dao.GaeaUserRoleOrgMapper;
import com.anji.plus.gaea.security.plus.modules.user.dao.entity.GaeaUserRoleOrg;
import com.anji.plus.gaea.utils.ApplicationContextUtils;
import com.anji.plus.gaea.utils.GaeaBeanUtils;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

/**
 * 组织(GaeaOrg)ServiceImpl
 *
 * @author lr
 * @since 2021-02-02 13:37:33
 */
@Service
public class GaeaOrgServiceImpl implements GaeaOrgService {

    @Autowired
    private GaeaOrgMapper gaeaOrgMapper;

    @Autowired
    private GaeaRoleMapper gaeaRoleMapper;

    @Autowired
    private GaeaUserRoleOrgMapper gaeaUserRoleOrgMapper;

    @Override
    public GaeaBaseMapper<GaeaOrg> getMapper() {
        return gaeaOrgMapper;
    }


    /**
     * 获取当前用户的机构编码列表，当有父机构时，可以包含子机构
     * @return
     */
    @Override
    public List<String> getCurrentUserOrgCodes() {
        List<TreeNode> treeNodes = tree(false);
        List<String> result = new ArrayList<>();
        return getOrgCodes(result, treeNodes);
    }


    public List<String> getOrgCodes(List<String> result, List<TreeNode> treeNodes) {
        treeNodes.stream().forEach(treeNode -> {
            result.add(treeNode.getId());
            if (!CollectionUtils.isEmpty(treeNode.getChildren())) {
                getOrgCodes(result, treeNode.getChildren());
            }
        });
        return result;
    }

    /**
     * 当机构编码不为空时，要查出当前组织的下属组织
     *
     * @param param   查询参数
     * @param queryWrapper 基本查询条件
     * @return
     */
    @Override
    public Wrapper<GaeaOrg> extensionWrapper(GaeaOrgParam param, QueryWrapper<GaeaOrg> queryWrapper) {
        if (StringUtils.isNotBlank(param.getCode())) {
            queryWrapper.and(wrapper ->wrapper.or().lambda().eq(GaeaOrg::getOrgParentCode, param.getCode()).or().eq(GaeaOrg::getOrgCode, param.getCode()));
        }
        return queryWrapper;
    }

    @Override
    public List<GaeaOrg> queryAllOrg(GaeaOrgParam gaeaOrgParam) {
        LambdaQueryWrapper<GaeaOrg> wrapper = Wrappers.lambdaQuery();
        wrapper.select(GaeaOrg::getOrgCode, GaeaOrg::getOrgName)
                .eq(GaeaOrg::getEnabled, Enabled.YES.getValue());
        if(gaeaOrgParam != null){
            if (StringUtils.isNotBlank(gaeaOrgParam.getOrgType())) {
                wrapper.eq(GaeaOrg::getOrgType, gaeaOrgParam.getOrgType());
            }
        }
        return gaeaOrgMapper.selectList(wrapper);
    }

    @Override
    public List<TreeNode> tree(boolean hasRole) {

        LambdaQueryWrapper<GaeaUserRoleOrg> roleOrgLambdaQueryWrapper = Wrappers.lambdaQuery();
        roleOrgLambdaQueryWrapper.eq(GaeaUserRoleOrg::getUsername, UserContentHolder.getUsername());
        List<GaeaUserRoleOrg> gaeaUserRoleOrgs = gaeaUserRoleOrgMapper.selectList(roleOrgLambdaQueryWrapper);


        //判断是否有超管角色
        Optional<GaeaUserRoleOrg> optional = gaeaUserRoleOrgs.stream().filter(gaeaUserRoleOrg -> gaeaUserRoleOrg.getRoleCode().equals(GaeaConstant.SUPER_ADMIN_ROLE)).findFirst();

        Set<String> orgCodes = new HashSet<>();
        List<GaeaOrg> orgList = new ArrayList<>();

        AtomicBoolean isSuperAdmin = new AtomicBoolean(false);

        //拥有超管角色
        if (optional.isPresent()) {
            LambdaQueryWrapper<GaeaOrg> orgWrapper = Wrappers.lambdaQuery();
            orgWrapper.eq(GaeaOrg::getEnabled, Enabled.YES.getValue());
            isSuperAdmin.set(true);
            orgList = gaeaOrgMapper.selectList(orgWrapper);

            orgCodes = orgList.stream().map(GaeaOrg::getOrgCode).collect(Collectors.toSet());
        } else {
            orgCodes = gaeaUserRoleOrgs.stream().map(GaeaUserRoleOrg::getOrgCode).collect(Collectors.toSet());
            //所有目录
            LambdaQueryWrapper<GaeaOrg> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.eq(GaeaOrg::getEnabled, Enabled.YES.getValue());
            queryWrapper.in(GaeaOrg::getOrgCode, orgCodes);
            orgList = gaeaOrgMapper.selectList(queryWrapper);
        }

        //机构角色对应关系,要求管理员即admin才能看到所有角色，其他不行
        Map<String, List<String>> orgRoleMap = gaeaUserRoleOrgs.stream()
                .collect(Collectors.groupingBy(GaeaUserRoleOrg::getOrgCode,
                        Collectors.mapping(GaeaUserRoleOrg::getRoleCode, Collectors.toList())));

        //机构和角色下的分组（机构对应角色列表）
        Map<String, List<TreeNode>> roleOrgMap = new HashMap<>(2);
        if (hasRole) {
            //查询所有启用角色
            LambdaQueryWrapper<GaeaRole> roleWrapper=Wrappers.lambdaQuery();
            roleWrapper.eq(GaeaRole::getEnabled, Enabled.YES.getValue());
            List<GaeaRole> gaeaRoles = gaeaRoleMapper.selectList(roleWrapper);

            //当包含角色时，添加机构与角色分组
            roleOrgMap.putAll(gaeaRoles.stream().filter(gaeaRole -> StringUtils.isNotBlank(gaeaRole.getOrgCode()))
                    .collect(Collectors.groupingBy(GaeaRole::getOrgCode, Collectors.mapping(gaeaRole -> {
                        TreeNode treeNode = new TreeNode();
                        treeNode.setId(gaeaRole.getOrgCode() + GaeaConstant.REDIS_SPLIT + gaeaRole.getRoleCode());
                        treeNode.setLabel(gaeaRole.getRoleName());
                        treeNode.setChildren(new ArrayList<>());
                        return treeNode;
                    }, Collectors.toList()))));
        }

        LambdaQueryWrapper<GaeaOrg> wrapper = Wrappers.lambdaQuery();
        wrapper.in(GaeaOrg::getOrgCode,orgCodes).or().in(GaeaOrg::getOrgParentCode, orgCodes);
        //root目录
        List<GaeaOrg> gaeaOrgs = gaeaOrgMapper.selectList(wrapper);

        List<TreeNode> result = orgList.stream().map(rootGaeaOrg -> {
            TreeNode treeNode = createTreeNode(gaeaOrgs, roleOrgMap, rootGaeaOrg,orgRoleMap, isSuperAdmin.get());
            return treeNode;
        }).collect(Collectors.toList());

        return result;
    }


    /**
     * 设置子机构
     *
     * @param treeNode
     * @param gaeaOrgList
     */
    private void setOrgChildren(TreeNode treeNode, List<GaeaOrg> gaeaOrgList, Map<String, List<TreeNode>> roleOrgMap,Map<String, List<String>> hasOrgRoleMap, boolean isSuperAdmin) {

        gaeaOrgList.stream()
                .filter(gaeaOrg -> StringUtils.equals(treeNode.getId(), gaeaOrg.getOrgParentCode()))
                .forEach(gaeaOrg -> {
                    TreeNode treeNodeTemp = createTreeNode(gaeaOrgList, roleOrgMap, gaeaOrg,hasOrgRoleMap,isSuperAdmin);
                    treeNode.getChildren().add(treeNodeTemp);
                });
    }

    private TreeNode createTreeNode(List<GaeaOrg> gaeaOrgList, Map<String, List<TreeNode>> roleOrgMap, GaeaOrg gaeaOrg,Map<String, List<String>> hasOrgRoleMap, boolean isSuperAdmin) {
        TreeNode treeNodeTemp = new TreeNode();
        treeNodeTemp.setId(gaeaOrg.getOrgCode());
        treeNodeTemp.setLabel(gaeaOrg.getOrgName());
        treeNodeTemp.setChildren(new ArrayList<>());
        //当组织与角色对应
        if (!CollectionUtils.isEmpty(roleOrgMap) && roleOrgMap.containsKey(gaeaOrg.getOrgCode())) {
            List<TreeNode> treeNodes = roleOrgMap.get(gaeaOrg.getOrgCode());

            List<String> hasRoles = hasOrgRoleMap.get(gaeaOrg.getOrgCode());

            if(isSuperAdmin || hasRoles.contains(GaeaConstant.SUPER_USER_NAME)) {
                //管理员可以看到所有角色
                treeNodeTemp.getChildren().addAll(treeNodes);
            } else {
                //非管理员只能看到自己的角色
                List<TreeNode> result = treeNodes.stream().filter(treeNode -> hasRoles.contains(treeNode.getId().split(GaeaConstant.REDIS_SPLIT)[1])).collect(Collectors.toList());
                treeNodeTemp.getChildren().addAll(result);
            }
        }
        //设置下级组织
        setOrgChildren(treeNodeTemp, gaeaOrgList, roleOrgMap, hasOrgRoleMap,isSuperAdmin);
        return treeNodeTemp;
    }

    @Override
    public void processAfterOperation(GaeaOrg entity, BaseOperationEnum operationEnum) throws BusinessException {
        ApplicationContextUtils.publishEvent(new OrgEvent(entity, operationEnum));
    }

    @Override
    public GaeaOrgDTO queryByOrgCode(String orgCode) {
        LambdaQueryWrapper<GaeaOrg> query = Wrappers.lambdaQuery();
        query.eq(GaeaOrg::getOrgCode, orgCode)
                .or().eq(GaeaOrg::getOrgParentCode, orgCode);

        GaeaOrg entity = gaeaOrgMapper.selectOne(query);
        GaeaOrgDTO gaeaOrgDTO = new GaeaOrgDTO();
        GaeaBeanUtils.copyAndFormatter(entity, gaeaOrgDTO);
        return gaeaOrgDTO;
    }

    @Override
    public Integer updateNameStatusByCode(GaeaOrgDTO gaeaOrgDTO) {
        String code = gaeaOrgDTO.getOrgCode();
        String orgFullName = gaeaOrgDTO.getOrgFullName();
        String name = gaeaOrgDTO.getOrgName();
        String updateBy = gaeaOrgDTO.getUpdateBy();
        if(StringUtils.isBlank(updateBy)){
            updateBy = "SYSTEM";
        }
        if(StringUtils.isBlank(orgFullName)){
            orgFullName = name;
        }
        LambdaQueryWrapper<GaeaOrg> query = Wrappers.lambdaQuery();
        query.eq(GaeaOrg::getOrgCode, code);

        GaeaOrg gaeaOrg = new GaeaOrg();
        gaeaOrg.setOrgName(name);
        gaeaOrg.setOrgFullName(orgFullName);
        gaeaOrg.setEnabled(gaeaOrgDTO.getEnabled());
        gaeaOrg.setUpdateBy(updateBy);
        gaeaOrg.setUpdateTime(new Date());

        Integer count = gaeaOrgMapper.update(gaeaOrg, query);
        return count;
    }

    @Override
    public Integer deleteByOrgCode(String orgCode) {
        LambdaQueryWrapper<GaeaOrg> query = Wrappers.lambdaQuery();
        query.eq(GaeaOrg::getOrgCode, orgCode)
                .or().eq(GaeaOrg::getOrgParentCode, orgCode);

        Integer count = gaeaOrgMapper.delete(query);
        return count;
    }
}
